package ro.qi.framework.sys.media;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;

import javax.imageio.ImageIO;

import ro.qi.framework.sys.debug.Logger;

import com.mortennobel.imagescaling.ResampleOp;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class Image {

	public static final int IMAGE_TYPE_UNKNOWN				= 0;
	public static final int IMAGE_TYPE_JPG					= 1;
	public static final int IMAGE_TYPE_GIF					= 2;
	public static final int IMAGE_TYPE_PNG					= 3;
	public static final int IMAGE_TYPE_BMP					= 4;
	public static final int IMAGE_TYPE_TIFF					= 5;

	private int type = Image.IMAGE_TYPE_UNKNOWN;
	
	private BufferedImage image = null;
	private OutputStream outputStream = null;
	
	public Image(String contentType, InputStream inputStream, OutputStream outputStream) {
		setContentType(contentType);
		image = decodeImage(inputStream);
		this.outputStream = outputStream;
	}
	
	public Image(String contentType, byte[] imageBytes, OutputStream outputStream) {
		setContentType(contentType);
		image = decodeImage(new ByteArrayInputStream(imageBytes));
		this.outputStream = outputStream;
	}
	
	public void resize(int width, int height) {
		if (image != null) {

			// get the dimension
	        int originalWidth = image.getWidth(null);
	        int originalHeight = image.getHeight(null);

	        int resizedWidth = 0;
	        int resizedHeight = 0;
	
	        if (width > 0 && height > 0) {
//        		Logger.debug(this, "originalWidth: " + originalWidth);
//        		Logger.debug(this, "originalHeight: " + originalHeight);
//        		Logger.debug(this, "originalWidth/originalHeight: " + ((float)originalWidth/(float)originalHeight));
//        		Logger.debug(this, "width/height: " + ((float)width/(float)height));
	        	if (((float)originalWidth/(float)originalHeight) != ((float)width/(float)height)) {
//	        		Logger.debug(this, "Requested width/height is different than image width/height.");
	        		if (originalWidth > originalHeight) {
			        	resizedWidth = width;
			        	resizedHeight = (width * originalHeight) / originalWidth;
//			        	System.out.println("1");
	        		} else {
	    	        	resizedWidth = (width * originalWidth) / originalHeight;
	    	        	resizedHeight = width;
//			        	System.out.println("2");
	        		}
	        	} else {
//	        		Logger.debug(this, "Requested width/height is equal with image width/height.");
		        	resizedWidth = width;
		        	resizedHeight = height;
	        	}
	        } else if (width > 0) {
	        	resizedWidth = width;
	        	resizedHeight = (width * originalHeight) / originalWidth;
	        } else if (height > 0) {
	        	resizedWidth = (height * originalWidth) / originalHeight;
	        	resizedHeight = height;
	        } else {
	        	return;
	        }
//	        Logger.debug(this, "Resized width: " + resizedWidth);
//	        Logger.debug(this, "Resized height: " + resizedHeight);
//	        if (originalWidth > originalHeight) {
//	        	resizedWidth = width;
//	        	resizedHeight = (width * originalHeight) / originalWidth;
//	        } else {
//	        	resizedWidth = (width * originalWidth) / originalHeight;
//	        	resizedHeight = width;
//	        }
	        
	        //BufferedImage sourceImage = ImageIO.read(new File(sourcefile));
	        ResampleOp resizeOp = new ResampleOp(resizedWidth, resizedHeight);
	        BufferedImage resizedBufferedImage = resizeOp.filter(image, null);
//	        ImageIO.write(resizedImage,"jpeg", new File(destfile));	        
//	
//	        java.awt.Image resizedImage = image.getScaledInstance(resizedWidth, resizedHeight, java.awt.Image.SCALE_SMOOTH);
//
//	        int imageType = BufferedImage.TYPE_INT_RGB;
//	        
//	        if (image.getType() > 0) {
//	        	imageType = image.getType(); 
//	        }
//	        BufferedImage resizedBufferedImage = new BufferedImage(resizedWidth, resizedHeight, imageType);
//	
//	        Graphics2D graphics2D = resizedBufferedImage.createGraphics();  
//			graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
//			graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
//			graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);		
//	//		graphics2D.drawImage(resizedImage, 0, 0, resizedWidth, resizedHeight, 0, 0, originalWidth, originalHeight, null);  
//			graphics2D.drawImage(resizedImage, 0, 0, null);  
//			graphics2D.dispose();  		
	
	        /*
			AffineTransform at = new AffineTransform();
			at.scale(2.0, 2.0);
			AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
			resizedBufferedImage = scaleOp.filter(originalBufferedImage, resizedBufferedImage);
			*/
	
			// Copy image to buffered image.
	//		Graphics2D graphics2D = bufferedImage.createGraphics();
	        // Clear background and paint the image.
	//		graphics2D.setColor(Color.white);
	//		graphics2D.fillRect(0, 0, resizedWidth, resizedHeight);
	//		graphics2D.drawImage(resizedImage, 0, 0, null);
	//		graphics2D.dispose();
	
	        // Soften.
//			if (getType() == IMAGE_TYPE_JPG && blurImage) {
//				float softenFactor = 0.05f;
//				float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0};
//				Kernel kernel = new Kernel(3, 3, softenArray);
//				ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
//				resizedBufferedImage = cOp.filter(resizedBufferedImage, null);
//			}

	//        ByteArrayOutputStream baos = new ByteArrayOutputStream();
	//        baos.flush();
	//        ImageIO.write(resizedBufferedImage, "jpeg", baos);
	//        baos.close();
	//		System.out.println("9 -" + new Date());
	//		return baos.toByteArray();
			if (resizedBufferedImage != null) {
				image = resizedBufferedImage;
			}
		}
	}

	private BufferedImage decodeImage(InputStream inputStream) {
		BufferedImage bufferedImage = null;
		try {
			switch (getType()) {
				case IMAGE_TYPE_JPG:
				{
					JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream);
					bufferedImage = decoder.decodeAsBufferedImage();
				}
				break;
				case IMAGE_TYPE_GIF:
				{
					bufferedImage = ImageIO.read(inputStream);
//					GifDecoder d = new GifDecoder(inputStream);
//					int err = d.read(inputStream);
//					bufferedImage = d.getImage();
				}
				break;
				case IMAGE_TYPE_PNG: throw new Exception("Unsuported format. PNG format is not suported.");
				case IMAGE_TYPE_BMP: throw new Exception("Unsuported format. BMP format is not suported.");
				case IMAGE_TYPE_TIFF: throw new Exception("Unsuported format. TIFF format is not suported.");
			}
		} catch (Exception exception) {
			Logger.error("Error decoding image: " + exception.getMessage());
		}
		return bufferedImage;
	}
	
	public void encodeImage(File cachedFile) {
		if (image != null && outputStream != null) {
			try {
				switch (getType()) {
					case IMAGE_TYPE_JPG:
					{
						JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
						JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
						param.setQuality(0.75f, false);
						encoder.encode(image);
						if (cachedFile != null) {
							Logger.debug(this, "Create file in local cache: " + cachedFile.getPath());
							ImageIO.write(image, "jpeg", cachedFile);
						}
					}
					break;
					case IMAGE_TYPE_GIF:
					{
						JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
						JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
						param.setQuality(0.75f, false);
						encoder.encode(image);
						if (cachedFile != null) {
							Logger.debug(this, "Create file in local cache: " + cachedFile.getPath());
							ImageIO.write(image, "jpeg", cachedFile);
						}
//						GifEncoder encoder = new GifEncoder(processedImage, outputStream);
//						encoder.encode();
//						if (getCachedFile() != null) {
//							Logger.debug(this, "Create file in local cache: " + cachedFile.getPath());
//							ImageIO.write(processedImage, "gif", cachedFile);
//						}
					}
				}
			} catch (Exception exception) {
				Logger.error("Error encoding image: " + exception.getMessage());
				exception.printStackTrace();
			}
		}
	}

	private void setContentType(String contentType) {
		if (contentType.equals("image/gif")) {
			setType(IMAGE_TYPE_GIF);
//			Logger.debug("Image type: GIF");
		} else if (contentType.equals("image/jp2") || contentType.equals("image/jpg") || contentType.equals("image/jpeg") || contentType.equals("image/pjpeg") || contentType.equals("image/pjpg")) {
			setType(IMAGE_TYPE_JPG);
//			Logger.debug("Image type: JPEG");
		} else if (contentType.equals("image/png")) {
			setType(IMAGE_TYPE_PNG);
//			Logger.debug("Image type: PNG");
		}
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

}
